package com.tianji.learning.service.impl;

import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constans.RedisConstants;
import com.tianji.learning.domain.vo.SignResultVO;
import com.tianji.learning.mq.message.SignInMessage;
import com.tianji.learning.service.ISignRecordService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.util.Arrays;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Slf4j
@Service
@RequiredArgsConstructor
public class SignRecordServiceImpl implements ISignRecordService {

    private final StringRedisTemplate redisTemplate;

    private final RabbitMqHelper mqHelper;

    @Override
    public SignResultVO addSignRecords() {
        // 1. 获取用户id
        Long user = UserContext.getUser();
        // 2. 拼接key
        LocalDate now = LocalDate.now();
        String format = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));

        String key = RedisConstants.SIGN_RECORD_KEY_PREFIX+user.toString()+format;
        // 3. 利用 bitSet命令保存签到命令 并校验返回值是否已经签到
        int offset = now.getDayOfMonth() - 1;
        Boolean setBit = redisTemplate.opsForValue().setBit(key, offset, true);
        if(setBit){
            throw new BadRequestException("不能重复签到");
        }
        // 4. 计算连续签到天数
        int days = countSignDays(key, offset + 1);

        // 5. 计算连续签到的奖励积分并保存
        int rewardPoints = 0;
        switch (days){
            case 7:
                rewardPoints=10; break;
            case 14:
                rewardPoints=20; break;
            case 28:
                rewardPoints=40; break;
        }

        // 6. 保存积分, 发送消息到MQ
        mqHelper.send(MqConstants.Exchange.LEARNING_EXCHANGE,
                MqConstants.Key.SIGN_IN,
                SignInMessage.of(user, rewardPoints+1));

        SignResultVO signResultVO = new SignResultVO();
        signResultVO.setSignPoints(1);
        signResultVO.setSignDays(days);
        signResultVO.setRewardPoints(rewardPoints);

        return signResultVO;
    }

    @Override
    public Byte[] querySignRecord() {
        // 1. 获取用户id
        Long user = UserContext.getUser();
        // 2. 获取当前日期
        int offset = LocalDate.now().getDayOfMonth();
        Byte[] ans = new Byte[offset];
        // 3. 拼接key
        LocalDate now = LocalDate.now();
        String format = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key = RedisConstants.SIGN_RECORD_KEY_PREFIX+user.toString()+format;
        // 4. 查询redis
        List<Long> longs = redisTemplate.opsForValue().bitField(key, BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(offset)).valueAt(0));
        if(CollUtils.isEmpty(longs)){
            return new Byte[0];
        }
        Long records = longs.get(0);
        // 6. 取数据
        offset -= 1;
        while (offset >= 0){
            ans[offset--] = (byte) (records & 1);
            records >>>= 1;
        }

        return ans;
    }

    /**
     * 计算连续签到的天数
     *
     */
    private int countSignDays(String key, int DayofMonth){
        List<Long> bitField = redisTemplate.opsForValue()
                .bitField(key,
                        BitFieldSubCommands
                                .create()
                                .get(BitFieldSubCommands.BitFieldType.unsigned(DayofMonth))
                                .valueAt(0));
        if(CollUtils.isEmpty(bitField)){
            return 0;
        }
        Long num = bitField.get(0);
        int cnt = 0;
        while((num & 1) ==1){
            ++cnt;
            num >>>= 1;
        }
        return cnt;
    }


}
